<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="https://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html">阮一峰讲解，很棒</a>
 <pre>
     闭包:
        一个函数和对其周围状态（lexical environment，词法环境）的引用捆绑在一起（或者说函数被引用包围），这样的组合就是闭包（closure）。
        也就是说，闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中，每当创建一个函数，闭包就会在函数创建的同时被创建出来。

     词法作用域

     /////////////////////////////////////////////////////////////////////////示例【词法作用域】
     let app = function (){
        let a = 1
        return function (){
            console.log(a)
            a++
        }
    }

    let a = 2
    let f = app()
    f()
    f()
    f()

    let fn = app()
    fn()
    fn()
    fn()
    fn()
    fn()


    app()()
    app()()
    app()()
    app()()
    /////////////////////////////////////////////////////////////////////////


     function init() {
        var name = "Mozilla"; // name 是一个被 init 创建的局部变量
        function displayName() { // displayName() 是内部函数，一个闭包
            alert(name); // 使用了父函数中声明的变量
        }
        displayName();
    }
    init();



     function makeFunc() {
        var name = "Mozilla";
        function displayName() {
            alert(name);
        }
        return displayName;
    }

    var myFunc = makeFunc();
    myFunc();



     function makeAdder(x) {
      return function(y) {
        return x + y;
      };
    }

    var add5 = makeAdder(5);
    var add10 = makeAdder(10);

    console.log(add5(2));  // 7
    console.log(add10(2)); // 12



    "<a href='#' id='size-12'>12</a>"
    '<a href='#' id='size-14'>14</a>'
    '<a href='#' id='size-16'>16</a>'
    function makeSizer(size) {
      return function() {
        document.body.style.fontSize = size + 'px';
      };
    }
    
    var size12 = makeSizer(12);
    var size14 = makeSizer(14);
    var size16 = makeSizer(16);
    document.getElementById('size-12').onclick = size12;
    document.getElementById('size-14').onclick = size14;
    document.getElementById('size-16').onclick = size16;


     用闭包模拟私有方法
        该共享环境创建于一个立即执行的匿名函数体内。这个环境中包含两个私有项：名为 privateCounter 的变量和名为 changeBy 的函数。
        这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。
     var Counter = (function() {
      var privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      }
    })();

    console.log(Counter.value()); /* logs 0 */
    Counter.increment();
    Counter.increment();
    console.log(Counter.value()); /* logs 2 */
    Counter.decrement();
    console.log(Counter.value()); /* logs 1 */


     性能考量
     如果不是某些特定任务需要使用闭包，在其它函数中创建函数是不明智的，因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响。

     bad
     function MyObject(name, message) {
      this.name = name.toString();
      this.message = message.toString();
      this.getName = function() {
        return this.name;
      };

      this.getMessage = function() {
        return this.message;
      };
    }


     good
     function MyObject(name, message) {
      this.name = name.toString();
      this.message = message.toString();
    }
    MyObject.prototype.getName = function() {
      return this.name;
    };
    MyObject.prototype.getMessage = function() {
      return this.message;
    };


     ///////////////////////////////////////////////////////面试题
     function fun(n,o) {
        console.log(o)
        return {
            fun:function(m){
                return fun(m,n);
            }
        };
    }
    var a = fun(0); //und
    a.fun(1); //0
    a.fun(2);
    a.fun(3);//undefined,?,?,?

    var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?

    var c = fun(0).fun(1);
    c.fun(2);
    c.fun(3);//undefined,?,?,?//问:三行a,b,c的输出分别是什么？

 </pre>
</body>
<script>

</script>
</html>